// --------------------------------------------------------------------------------------------------------------------- 
// <copyright file="ResourceHelper.cs" company="Scrum for Team System">
//   None
// </copyright>
// <summary>
//   Defines the ResourceHelper type.
// </summary>
// ---------------------------------------------------------------------------------------------------------------------
namespace ScrumForTeamSystem.EventService.Infrastructure
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Xml;
    using System.Xml.Schema;

    /// <summary>
    /// Defines the ResourceHelper type.
    /// </summary>
    public static class ResourceHelper
    {
        /// <summary>
        /// Loads the rules from the specified path..
        /// </summary>
        /// <typeparam name="T">The rule collection type.</typeparam>
        /// <param name="fileName">The file path.</param>
        /// <param name="schemaStream">The schema stream.</param>
        /// <param name="rules">The rules.</param>
        /// <returns>
        /// <c>True</c> if the rules loaded; otherwise <c>false</c>.
        /// </returns>
        public static bool TryLoadRules<T>(string fileName, Stream schemaStream, out T rules) where T : class, new()
        {
            var path = GetRulesFilePath(fileName);

            ValidateRulesFile(path, schemaStream);

            var serialiser = new SerializerInstance<T>();

            using (var sr = new StreamReader(path))
            {
                rules = serialiser.Deserialize(sr.ReadToEnd());
            }

            return rules != null;
        }

        /// <summary>
        /// Gets the rules file path.
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        /// <returns>get rules file path</returns>
        private static string GetRulesFilePath(string fileName)
        {
            if (string.IsNullOrEmpty(fileName))
            {
                throw new ArgumentNullException("fileName");
            }

            var path = fileName;

            if (File.Exists(path))
            {
                return path;
            }

            path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName);

            if (File.Exists(path))
            {
                return path;
            }

            path = Path.Combine(AppDomain.CurrentDomain.RelativeSearchPath, fileName);

            if (File.Exists(path))
            {
                return path;
            }

            throw new FileNotFoundException(string.Format(CultureInfo.InvariantCulture, "Rules file: '{0}' not found.", fileName));
        }

        /// <summary>
        /// Validates the rules file.
        /// </summary>
        /// <param name="path">The file path.</param>
        /// <param name="schemaStream">The stream.</param>
        private static void ValidateRulesFile(string path, Stream schemaStream)
        {
            var failures = new List<string>();

            var readerSettings = new XmlReaderSettings();

            readerSettings.Schemas.Add(null, XmlReader.Create(schemaStream));
            readerSettings.ValidationType = ValidationType.Schema;
            readerSettings.ValidationEventHandler +=
                    (sender, e) =>
                    {
                        if (e.Severity == XmlSeverityType.Warning)
                        {
                            failures.Add(string.Concat("Warning: ", e.Message));
                        }

                        if (e.Severity == XmlSeverityType.Error)
                        {
                            failures.Add(string.Concat("Error: ", e.Message));
                        }
                    };

            using (var rules = XmlReader.Create(path, readerSettings))
            {
                while (rules.Read())
                {
                }
            }

            if (failures.Count() != 0)
            {
                var concatFailures = string.Concat(failures.Select(s => string.Concat(s, "\r\n")).ToArray());

                throw new XmlSchemaValidationException(
                        string.Format(
                                CultureInfo.InvariantCulture,
                                "Schema validation failed for file: '{0}'\r\n{1}",
                                path,
                                concatFailures));
            }
        }
    }
}